#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <vector>
#include <ctime>
#include "task.hpp"
#include "sys/wait.h"
// ctrl ~ 调出云服务器 执行命令的界面 快捷键
using namespace std;

// 用枚举常量 设置错误码
enum
{
    UsageError = 1,
    ArgError,
    PipeError
};

// 描述通信信道的类
class Channel
{
public:
    Channel(int wfd, pid_t sud_id, const string &name)
        : _wfd(wfd), _sub_process_id(sud_id), _name(name)
    {
    }
    ~Channel()
    {
    }
    string name() { return _name; }
    int wfd() { return _wfd; }
    int pid() { return _sub_process_id; }
    void Close() { close(_wfd); }
    void PrintDebug()
    {
        cout << "_wfd: " << _wfd << "，";
        cout << "_sub_process_id: " << _sub_process_id << "，";
        cout << "_name: " << _name << endl;
    }

private:
    int _wfd;
    pid_t _sub_process_id;
    string _name;
};

void Usage(const std::string &proc)
{
    cout << "Usage: " << proc << " subprocess-num" << endl;
}

class ProcessPool
{
public:
    ProcessPool(int sub_process_num) : _sub_process_num(sub_process_num)
    {
    }

    // 创建通信信道和子进程
    int CreatProcess(work_t work)
    {
        for (int number = 0; number < _sub_process_num; number++)
        {
            // 创建管道
            int pipefd[2]{0};
            int n = pipe(pipefd);
            if (n < 0)
                return PipeError;
            // 用一个vector来存历史上打开的写端fd
            std::vector<int> fds;
            // 创建子进程
            pid_t id = fork();
            if (id == 0)
            {
                // 关闭继承得来的父进程历史上打开的所有写端fd
                if (!fds.empty())
                {
                    std::cout << "close w fd : ";
                    for (auto fd : fds)
                    {
                        std::cout << fd << " ";
                        close(fd);
                    }
                    std::cout << std::endl;
                }
                sleep(2);
                // child -> r
                close(pipefd[1]); // 关闭子进程的写端

                // 执行任务
                dup2(pipefd[0], 0); // 重定向，使子进程本来应该从标准输入0里读数据，改为从pipefd[0],即从管道中读
                work(pipefd[0]);

                exit(0);
            }

            string cname = "channel-" + to_string(number);
            // father -> w
            close(pipefd[0]);
            // 把父进程历史上打开的所有写端存起来
            fds.push_back(pipefd[1]);
            channels.push_back(Channel(pipefd[1], id, cname));
        }
        return 0;
    }

    // 选择下一个channel 要负载均衡的选择     轮循
    int NextChannel()
    {
        static int next = 0;     // 只会初始化一次
        int c = next++;          // next++ 就会选择下一个
        next %= channels.size(); // 防止越界
        return c;
    }

    void SendTaskCode(int index, uint32_t code)
    {
        // 打印一段日志，输出选择的通信信道和进程的名字
        cout << "send code: " << code << " to " << channels[index].pid() << endl;
        write(channels[index].wfd(), &code, sizeof(code));
    }

    // 让所有子进程  把写端全部关闭掉，进程就会读到管道中的结束位置，即read函数会返回0，子进程终止工作，回到回调函数的那里，在执行下一句代码就是exit退出进程
    void KillAll()
    {
        for (auto &channel : channels)
        {
            channel.Close();
            std::cout << channel.name() << " close done" << "sub process quit now : " << channel.pid() << std::endl;
        }
    }
    void Wait()
    {
        for (auto &channel : channels)
        {
            pid_t pid = channel.pid();
            pid_t rid = waitpid(pid, nullptr, 0);
            if (rid == pid)
            {
                std::cout << "Wait sub process: " << pid << " success..." << std::endl;
            }
        }
    }
    void Debug()
    {
        for (auto &channel : channels)
        {
            channel.PrintDebug();
        }

        sleep(1);
    }

    ~ProcessPool()
    {
    }

private:
    int _sub_process_num;
    vector<Channel> channels;
};

void CtrlProcessPool(ProcessPool *processpool_ptr, int cnt)
{
    while (cnt)
    {
        // a. 选择一个进程和通道
        //    channel就是选择的通道的下标，因为已经用一个vector把创建好的argv[1]个channel(通道)给管理好了
        int channel = processpool_ptr->NextChannel();

        sleep(1);

        // cout << channel.name() << endl;
        // b. 你要选择一个任务
        //    NextTask()函数会随机返回一个小于3的数，因为只写了3个不同的任务，即code就是需要执行任务的下标
        uint32_t code = NextTask();

        // c. 发送任务
        processpool_ptr->SendTaskCode(channel, code); // 把选择好的通道和进程发送过去

        sleep(1); // 每隔一秒发送一个任务

        cnt--;
    }
}

// ./processpool 5    int argc(后面数组的元素个数) char *argv[] (argv[0]是可执行程序名字  argv[1]是要生成的子进程个数)
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return UsageError;
    }
    int sub_process_num = std::stoi(argv[1]);
    if (sub_process_num <= 0)
        return ArgError;

    // 随机数

    // 容器用于管理 通信信道
    vector<Channel> channels;

    // 1、 创建通信信道和子进程
    ProcessPool *processpool_ptr = new ProcessPool(sub_process_num);
    processpool_ptr->CreatProcess(worker);
    processpool_ptr->Debug();
    // 2、 控制子进程
    CtrlProcessPool(processpool_ptr, 10);

    std::cout << "task run done" << std::endl;
    // sleep(100);

    // 3、 子进程  a.你怎么让所有的子进程退出？
    processpool_ptr->KillAll();
    // b.你怎么让所有已退出的子进程把他进行资源回收wait/waitpid
    // sleep(100);
    // wait sub process
    processpool_ptr->Wait();

    delete processpool_ptr;
    return 0;
}